package com.cango.student.methodHandle;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class MethodHandleTest {

    public MethodHandle getHandler() {
        MethodHandle mh = null;
        // 在获取方法句柄之前，先通过MethodType的静态工厂方法，先生成一个包含方法参数类型、方法返回类型的的方法类型，
        // 也就是MethodType mt = MethodType.methodType(String.class, int.class, int.class)。
        MethodType mt = MethodType.methodType(String.class, int.class, int.class);
        // 获取方法句柄要用到Lookup对象，比如代码中的MethodHandles.Lookup lk，这个对象可以提供其所在环境中任何可见方法的方法句柄
        // 我们可以将其比喻成包含有某个类对象的方法成员、方法的容器，
        MethodHandles.Lookup lk = MethodHandles.lookup();

        try {
            // 通过lk.findVirtual(String.class, "substring", mt);具体锁定String类型中的某个方法，作为方法句柄返回
            // 要从lookup对象中得到方法句柄，需要给出持有所需方法的类，方法的名称，以及跟方法相匹配的方法类型
            mh = lk.findVirtual(String.class, "substring", mt);
        } catch (Throwable e) {
            e.printStackTrace();
        }

        return mh;
    }

    public static void main(String[] args) throws Throwable {
        MethodHandle mh = new MethodHandleTest().getHandler();
        String str = "hello world";

        // 方法的调用参数基本都一样，第一个参数为方法的接受对象，即是哪个对象执行这个方法，
        // 接下来的参数就是执行方法所需要的参数。
        // 当然第一个参数，也就是方法的接受对象，可以通过方法句柄的bindto动态的参数绑定方法来绑定，从而使方法句柄的调用和普通方法调用没区别，动态的参数绑定我们以后再研究
        Object result1 = mh.invoke(str, 1, 3);
        Object result2 = (String) mh.invokeExact(str, 1, 3);
//      Object result2 = mh.invokeExact(str, new Integer(1), 3);
        /**
         * 上面这句方法执行时报错,因为方法类型为String.class, int.class, int.class
         * 而返回的类型为Object，与声明中为String不符合
         * 其中第二个参数类型为Integer，与声明中为int不符合，则类型适配不符合，系统报错。
         */
        // 静态方法和动态方法之间的差别，静态方法是不需要制定方法的接受对象的，而一般方法是需要的

        System.out.println("result 1:" + result1);
        System.out.println("result 1:" + result2);
    }
}
